#ifndef TB_TYPE_TRAITS_HPP__
#define TB_TYPE_TRAITS_HPP__

#include <boost/mpl/vector.hpp>
#include <boost/mpl/contains.hpp>

#include "tbcore/base/basic_types.hpp"
#include "tbcore/base/duration.hpp"
#include "tbcore/base/datetime.hpp"
#include "tbcore/base/string.hpp"

#include "tbcore/config.hpp"

#include "reflection_fwd.hpp"

TB_NAMESPACE_BEGIN

template <typename T1 = NoneType, typename T2 = NoneType, typename T3 = NoneType, typename T4 = NoneType, typename T5 = NoneType,
          typename T6 = NoneType, typename T7 = NoneType, typename T8 = NoneType, typename T9 = NoneType, typename T10 = NoneType,
          typename T11 = NoneType, typename T12 = NoneType, typename T13 = NoneType, typename T14 = NoneType, typename T15 = NoneType>
struct TypeList {
  typedef T1 head_type;
  typedef TypeList<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> tail_type;
};

template <typename T1>
struct TypeListSize {
  enum { size = TypeListSize<typename T1::tail_type>::size + 1 };
};

template <>
struct TypeListSize<TypeList<NoneType> > {
  enum { size = 0 };
};

template <typename T, unsigned int N>
struct TypeListAt {
  typedef typename TypeListAt<typename T::tail_type, N-1>::type type;
};

template <typename T>
struct TypeListAt<T, 0> {
  typedef typename T::head_type type;
};

typedef boost::mpl::vector<bool, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float, double> SimpleTypeVec;

typedef boost::mpl::vector<bool, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float, double, _STD string, string16, Duration, DateTime, UniqueId> BuildinTypeVec;

template <typename T>
struct remove_crv {
  typedef typename remove_cv<typename remove_reference<T>::type>::type type;
};

template <typename Type, typename Enabled = void>
struct VariantTrait {
  enum { VariantId = 0 };
  static Type Default() { return Type(); }
};

template <typename T>
struct is_simple_type 
  : boost::mpl::eval_if<boost::mpl::contains<SimpleTypeVec, T>, true_type, false_type>::type {};

template <typename T>
struct is_buildin_type 
  : boost::mpl::eval_if<boost::mpl::contains<BuildinTypeVec, T>, true_type, false_type>::type {};

template <typename T, typename Enabled = void> 
struct is_smart_ptr
  : false_type {};

template <typename T> struct
is_smart_ptr<T, typename enable_if<is_same<shared_ptr<typename remove_crv<typename T::element_type>::type>, typename remove_crv<T>::type>::value >::type>
  : true_type {};

template <typename T, typename Enabled = void>
struct is_user_object
  : false_type {};

template <typename T> uint32 GetVariantTypeId(const void* p);

template <typename T, typename Enabled = void> struct
GetVariantTypeIdHelper {
	static uint32 TypeId(const void* p) {
		boost::ignore_unused(p);
		return VariantTrait<T>::VariantId;
	}
};

template <typename T> struct
GetVariantTypeIdHelper < T, typename enable_if<is_array<T>::value >::type > {
	typedef typename remove_crv<T>::type type;
	static uint32 TypeId(const void* p) {
		(void)p;
		static const uint32 typeId =
			reflection::RegisterType(nullptr, "");
		return typeId;
	}
};

template <> struct
GetVariantTypeIdHelper < void > {
	static uint32 TypeId(const void* p) {
		boost::ignore_unused(p);
		return 0;
	}
};

template <typename T> struct 
GetVariantTypeIdHelper<T, typename enable_if<is_smart_ptr<T>::value >::type> {
  typedef typename remove_crv<T>::type type;
  static uint32 TypeId(const void* p) {
    return GetVariantTypeId<typename T::element_type>(p ? reinterpret_cast<const VoidPtr*>(p)->get() : nullptr);
  }
};

template <typename T>
uint32 GetVariantTypeId(const void* p = nullptr) {
  typedef typename remove_crv<T>::type type;
  return GetVariantTypeIdHelper<T>::TypeId(p);
}

template <typename T>
const void* GetAddress(const T& val) {
  return &val;
}

template <typename T>
const void* GetAddress(const boost::shared_ptr<T>& val) {
  return val.get();
}

template <typename T>
struct ArrayTrait {
	typedef void element_type;
	enum { size = 0 };
};

template <typename T, uint32 N>
struct ArrayTrait<T[N]> {
	typedef T element_type;
	enum { size = N };
};

template <typename T>
struct ArrayTrait < T[] > {
	typedef T element_type;
	enum { size = 0 };
};

//////////////////////////////////////////////////////////////////////////
// check method for member function
// if we have class definition:
// class MyClass {
//   ...
//   RetType FuncName(ParamType1 p1, ParamType2 p2);
//   ...
// }
// then 
//   BOOST_STATIC_ASSERT(check_method<RetType (MyClass::*)(ParamType1, ParamType2), Class::FuncName>::type);
// may pass
//////////////////////////////////////////////////////////////////////////

template <typename T, T> struct 
check_method
  : true_type {};

template <typename T>
struct TypeIsMutable
  : boost::mpl::eval_if<boost::mpl::and_<is_reference<T>, boost::mpl::not_<is_const<T> > >, true_type, false_type>::type {};

TB_NAMESPACE_END

#endif //TB_TYPE_TRAITS_HPP__
